package com.kaibes.core.base.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.kaibes.core.base.annotation.Description;
import com.kaibes.core.base.annotation.Ignore;
import com.kaibes.core.base.core.FieldData;

public class ClassUtils {
    
    public static boolean hasInterface(Class<?> clazz, Class<?>[] interfaces) {
        for (Class<?> classT : interfaces) {
            if (!classT.isAssignableFrom(clazz)) {
                return false;
            }
        }
        return true;
    }
    
    public static boolean isAnnotationPresent(Class<?> clazz, Class<? extends Annotation>[] annotations) {
        for (Class<? extends Annotation> classT : annotations) {
            if (!clazz.isAnnotationPresent(classT)) {
                return false;
            }
        }
        return true;
    }
    
    public static Field getField(Class<?> clazz, String fieldName) {
        try {
            Field field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
            return field;
        } catch (NoSuchFieldException | SecurityException e) {
            return null;
        }
    }

    public static String getSimpleName(String className) {
        try {
            return Class.forName(className).getSimpleName();
        } catch (ClassNotFoundException e) {
            return "";
        }
    }
    
    public static String getDescription(String className) {
        try {
            if (className.contains("<")) {
                className = className.substring(0, className.indexOf("<"));
            }
            Class<?> clazz = Class.forName(className);
            if (List.class.isAssignableFrom(clazz)) {
                return "数组结构";
            }
            String result = clazz.getSimpleName();
            if (clazz.isAnnotationPresent(Description.class)) {
                result = clazz.getDeclaredAnnotation(Description.class).value();
            }
            return result;
        } catch (ClassNotFoundException e) {
            return "";
        }
    }
    
    public static String getRemark(String className) {
        try {
            Class<?> clazz = Class.forName(className);
            String clazzRemark = "";
            if (clazz.isAnnotationPresent(Description.class)) {
                clazzRemark = clazz.getDeclaredAnnotation(Description.class).value();
            }
            return clazzRemark;
        } catch (ClassNotFoundException e) {
            return "";
        }
    }
    
    public static List<Field> getIntegerFieldList(Class<?> clazz) {
        Field[] fields = clazz.getDeclaredFields();
        List<Field> fieldList = new ArrayList<>();
        for (Field field : fields) {
            if (field.getType() == Integer.class) {
                field.setAccessible(true);
                fieldList.add(field);
            }
        }
        return fieldList;
    }
    
    public static List<Field> getStringFieldList(Class<?> clazz) {
        Field[] fields = clazz.getDeclaredFields();
        List<Field> fieldList = new ArrayList<>();
        for (Field field : fields) {
            if (field.getType() == String.class) {
                field.setAccessible(true);
                fieldList.add(field);
            }
        }
        return fieldList;
    }

    public static boolean looksLike(Class<?> source, Class<?> target) {
        boolean flag = true;
        Method[] methods = target.getMethods();
        for (Method method : methods) {
            try {
                source.getMethod(method.getName(), method.getParameterTypes());
            } catch (NoSuchMethodException | SecurityException e) {
                flag = false;
                break;
            }
        }
        return flag;
    }
    
    public static boolean isBasicClass(Class<?> clazz) {
        return  Number.class.isAssignableFrom(clazz) ||
                clazz==Integer.TYPE||clazz==Long.TYPE||clazz==String.class||
                clazz==Byte.TYPE||clazz==Short.TYPE||clazz==Float.TYPE||clazz==Double.TYPE||
                clazz==Boolean.TYPE||clazz==Boolean.class||
                clazz==Character.TYPE||clazz==Character.class||
                clazz==LocalDate.class||clazz==LocalDateTime.class;
    }
    
    public static boolean isBasicClass(String className) {
        try {
            return isBasicClass(Class.forName(className));
        } catch (ClassNotFoundException e) {
            return false;
        }
    }
    
//    public static boolean isBasicClass(Class<?> clazz) {
//        if (clazz.isPrimitive() || clazz.getSuperclass() == Number.class) {
//            return true;
//        } else if (clazz.getSuperclass() == Object.class) {
//            Class<?>[] interfaces = clazz.getInterfaces();
//            if (interfaces.length==2 && (interfaces[0]==Serializable.class && interfaces[1]==Comparable.class)) {
//                return true;
//            } else {
//                return false;
//            }
//        } else {
//            return false;
//        }
//    }
    
    @SuppressWarnings("unchecked")
    public static List<FieldData> getListFieldData(Class<?> clazz) {
        if (clazz == null) {
            return Collections.EMPTY_LIST;
        }

        List<FieldData> fieldDataList = new ArrayList<FieldData>();
        if (String.class.isAssignableFrom(clazz)) {
            FieldData fieldData = new FieldData();
            fieldData.setName("基础类型");
            fieldData.setRemark("数据是字符类型");
            fieldData.setType(String.class.getSimpleName());
            fieldData.setTypeClass(String.class.getName());
            fieldDataList.add(fieldData);
        } else if (Number.class.isAssignableFrom(clazz)) {
            FieldData fieldData = new FieldData();
            fieldData.setName("基础类型");
            fieldData.setRemark("数据是数字类型");
            fieldData.setType(clazz.getSimpleName());
            fieldData.setTypeClass(clazz.getName());
            fieldDataList.add(fieldData);
        } else {
            Field[] fields = clazz.getDeclaredFields();
            if (clazz.isEnum()) {
                for (Field field : fields) {
                    if (field.isAnnotationPresent(Ignore.class)) {
                        continue;
                    }
                    if (field.isEnumConstant() && !field.isAnnotationPresent(Ignore.class)) {
                        FieldData data = new FieldData();
                        data.setName(field.getName());
                        data.setType(Enum.class.getSimpleName());
                        data.setTypeClass(Enum.class.getName());
                        if (field.isAnnotationPresent(Description.class)) {
                            data.setRemark(field.getDeclaredAnnotation(Description.class).value());
                        }
                        fieldDataList.add(data);
                    }
                }
            } else {
                if (clazz.getSuperclass() != null && clazz.getSuperclass() != Object.class) {
                    fieldDataList.addAll(getListFieldData(clazz.getSuperclass()));
                }
                for (Field field : fields) {
                    if (field.isAnnotationPresent(Ignore.class)) {
                        continue;
                    }
                    if (!field.isSynthetic() && !field.isAnnotationPresent(Ignore.class)) {
                        FieldData data = new FieldData();
                        data.setName(field.getName());
                        data.setType(field.getType().getSimpleName());
                        data.setTypeClass(field.getType().getName());
                        if (field.isAnnotationPresent(Description.class)) {
                            data.setRemark(field.getDeclaredAnnotation(Description.class).value());
                        }
                        fieldDataList.add(data);
                    }
                }
            }
        }

        return fieldDataList;
    }
    
    public static Method findMethodByName(Method[] methods, String name) {
        for (int j = 0; j < methods.length; j++) {
            if (methods[j].getName().equals(name))
                return methods[j];
        }
        return null;
    }
    
}
